-- default/furnace.lua

-- support for MT game translation.
local S = default.get_translator

--
-- Formspecs
--

function default.get_furnace_active_formspec(fuel_percent, item_percent)
   return 'size[8,8.5]'..
      'list[context;src;2.75,0.5;1,1;]'..
      'list[context;fuel;2.75,2.5;1,1;]'..
      'image[2.75,1.5;1,1;default_furnace_fire_bg.png^[lowpart:'..
      (fuel_percent)..':default_furnace_fire_fg.png]'..
      'image[3.75,1.5;1,1;gui_furnace_arrow_bg.png^[lowpart:'..
      (item_percent)..':gui_furnace_arrow_fg.png^[transformR270]'..
      'list[context;dst;4.75,0.96;2,2;]'..
      'list[current_player;main;0,4.25;8,1;]'..
      'list[current_player;main;0,5.5;8,3;8]'..
      'listring[context;dst]'..
      'listring[current_player;main]'..
      'listring[context;src]'..
      'listring[current_player;main]'..
      'listring[context;fuel]'..
      'listring[current_player;main]'..
      default.get_hotbar_bg(0, 4.25)
end

function default.get_furnace_inactive_formspec()
   return 'size[8,8.5]'..
      'list[context;src;2.75,0.5;1,1;]'..
      'list[context;fuel;2.75,2.5;1,1;]'..
      'image[2.75,1.5;1,1;default_furnace_fire_bg.png]'..
      'image[3.75,1.5;1,1;gui_furnace_arrow_bg.png^[transformR270]'..
      'list[context;dst;4.75,0.96;2,2;]'..
      'list[current_player;main;0,4.25;8,1;]'..
      'list[current_player;main;0,5.5;8,3;8]'..
      'listring[context;dst]'..
      'listring[current_player;main]'..
      'listring[context;src]'..
      'listring[current_player;main]'..
      'listring[context;fuel]'..
      'listring[current_player;main]'..
      default.get_hotbar_bg(0, 4.25)
end

--
-- Node callback functions that are the same for active and inactive furnace
--

local function can_dig(pos, player)
   local meta = minetest.get_meta(pos);
   local inv = meta:get_inventory()
   return inv:is_empty('fuel') and inv:is_empty('dst') and inv:is_empty('src')
end

function default.is_hot_node(name)
   if name=='default:lava_source' or name=='hopper:hopper' then
      return true
   else
      return false
   end
end

local function param2_to_front(param2)
   if param2==0 then
      return {x=0,y=0,z=-1}
   elseif param2==1 then
      return {x=-1,y=0,z=0}
   elseif param2==2 then
      return {x=0,y=0,z=1}
   elseif param2==3 then
      return {x=1,y=0,z=0}
   end
end

local function allow_metadata_inventory_put(pos, listname, index, stack, player)
   if minetest.is_protected(pos, player:get_player_name()) then
      return 0
   end
   local meta = minetest.get_meta(pos)
   local inv = meta:get_inventory()
   if listname == 'fuel' then
      if minetest.get_craft_result({method='fuel', width=1, items={stack}}).time ~= 0 then
         if inv:is_empty('src') then
            meta:set_string('infotext', S('Furnace is empty'))
         end
         return stack:get_count()
      else
         return 0
      end
   elseif listname == 'src' then
      return stack:get_count()
   elseif listname == 'dst' then
      return 0
   end
end

function default.is_super_furnace(pos)--function to check if a furnace should smelt extra quickly
   local node = minetest.get_node(pos)
   if node.name~='default:furnace' and node.name~='default:furnace_active' then --not even a furnace
      return false
   end
   if node.param2 > 3 then --seriously? just have it face a reasonable direction people.
      return false
   end
   -- ok, so we check from pos + {x=1,y=1,z=1} to pos + {x=-1,y=-2,z=-1} for heat_shield
   -- except pos+{x=0,y=-1,z=0} has to be hot_node (lava, fire, etc.)
   -- and pos+param2_to_front(node.param2) has to be air
   local underpos = vector.add(pos,{x=0,y=-1,z=0})
   local frontpos = vector.add(pos,param2_to_front(node.param2))
   if not default.is_hot_node(minetest.get_node(underpos).name) then-- nothing to keep us extra hot.
      return false
   end
   if not minetest.get_node(frontpos).name=='air' then --we need somewhere to vent
      return false
   end
   local pos1 = {x=pos.x+1, y=pos.y, z=pos.z+1}
   local pos0 = {x=pos.x-1, y=pos.y-2, z=pos.z-1}
   local can_replace = minetest.find_nodes_in_area(pos0, pos1, 'default:lava_source')
   local replace_num = #can_replace
   if replace_num < 12 then
      return false
   end
   return true --yay! this furnace has succeded and can smelt extra quickly!
end

local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
   local meta = minetest.get_meta(pos)
   local inv = meta:get_inventory()
   local stack = inv:get_stack(from_list, from_index)
   return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
end

local function allow_metadata_inventory_take(pos, listname, index, stack, player)
   if minetest.is_protected(pos, player:get_player_name()) then
      return 0
   end
   return stack:get_count()
end

local function swap_node(pos, name)
   local node = minetest.get_node(pos)
   if node.name == name then
      return
   end
   node.name = name
   minetest.swap_node(pos, node)
end

local function custom_cooking_check(itemlist,pos)
   local meta = minetest.get_meta(pos)
   local super_furnace = meta:get_string('super')
   local cooked,aftercooked
   cooked,aftercooked = minetest.get_craft_result({method = 'cooking', width = 1, items = itemlist})
   if super_furnace == 'yes' then
      cooked.time = cooked.time/2 --or whatever speed increase u think is reasonable.
   end
   return cooked,aftercooked
end

local function furnace_node_timer(pos, elapsed)
   -- Initialize metadata
   --
   local meta = minetest.get_meta(pos)
   local fuel_time = meta:get_float('fuel_time') or 0
   local src_time = meta:get_float('src_time') or 0
   local fuel_totaltime = meta:get_float('fuel_totaltime') or 0
   local super_furnace = meta:get_string('super')
   if super_furnace == 'maybe' then
      if default.is_super_furnace(pos) then
         meta:set_string('super', 'yes')
      else
         meta:set_string('super', 'no')
      end
   end
   local inv = meta:get_inventory()
   local srclist, fuellist
   local dst_full = false

   local cookable, cooked
   local fuel

   local update = true
   while elapsed > 0 and update do
      update = false
      srclist = inv:get_list('src')
      fuellist = inv:get_list('fuel')

      --
      -- Cooking
      --

      -- Check if we have cookable content
      local aftercooked
      cooked, aftercooked = custom_cooking_check(srclist,pos)
      cookable = cooked.time ~= 0

      local el = math.min(elapsed, fuel_totaltime - fuel_time)
      if cookable then -- fuel lasts long enough, adjust el to cooking duration
         el = math.min(el, cooked.time - src_time)
      end

      -- Check if we have enough fuel to burn
      if fuel_time < fuel_totaltime then
         -- The furnace is currently active and has enough fuel
         fuel_time = fuel_time + el
         -- If there is a cookable item then check if it is ready yet
         if cookable then
            src_time = src_time + el
            if src_time >= cooked.time then
               -- Place result in dst list if possible
               if inv:room_for_item('dst', cooked.item) then
                  inv:add_item('dst', cooked.item)
                  inv:set_stack('src', 1, aftercooked.items[1])
                  src_time = src_time - cooked.time
                  update = true
               else
                  dst_full = true
               end
            else
               -- Item could not be cooked: probably missing fuel
               update = true
            end
         end
      else
         -- Furnace ran out of fuel
         if cookable then
            -- We need to get new fuel
            local afterfuel
            fuel, afterfuel = minetest.get_craft_result({method = 'fuel', width = 1, items = fuellist})

            if fuel.time == 0 then
               -- No valid fuel in fuel list
               fuel_totaltime = 0
               src_time = 0
            else
               -- Take fuel from fuel list
               inv:set_stack('fuel', 1, afterfuel.items[1])
               -- Put replacements in dst list or drop them on the furnace.
               local replacements = fuel.replacements
               if replacements[1] then
                  local leftover = inv:add_item('dst', replacements[1])
                  if not leftover:is_empty() then
                     local above = vector.new(pos.x, pos.y + 1, pos.z)
                     local drop_pos = minetest.find_node_near(above, 1, {'air'}) or above
                     minetest.item_drop(replacements[1], nil, drop_pos)
                  end
               end
               update = true
               fuel_totaltime = fuel.time + (fuel_totaltime - fuel_time)
            end
         else
            -- We don't need to get new fuel since there is no cookable item
            fuel_totaltime = 0
            src_time = 0
         end
         fuel_time = 0
      end

      elapsed = elapsed - el
   end

   if fuel and fuel_totaltime > fuel.time then
      fuel_totaltime = fuel.time
   end
   if srclist and srclist[1]:is_empty() then
      src_time = 0
   end
   --
   -- Update formspec, infotext and node
   --
   local formspec
   local item_state
   local item_percent = 0
   if cookable then
      item_percent = math.floor(src_time / cooked.time * 100)
      if dst_full then
         item_state = S('100% (output full)')
      else
         item_state = S('@1%', item_percent)
      end
   else
      if srclist and not srclist[1]:is_empty() then
         item_state = S('Not cookable')
      else
         item_state = S('Empty')
      end
   end

   local fuel_state = S('Empty')
   local active = false
   local result = false

   if fuel_totaltime ~= 0 then
      active = true
      local fuel_percent = 100 - math.floor(fuel_time / fuel_totaltime * 100)
      fuel_state = S('@1%', fuel_percent)
      formspec = default.get_furnace_active_formspec(fuel_percent, item_percent)
      swap_node(pos, 'default:furnace_active')
      -- make sure timer restarts automatically
      result = true
   else
      if fuellist and not fuellist[1]:is_empty() then
         fuel_state = S('@1%', 0)
      end
      formspec = default.get_furnace_inactive_formspec()
      swap_node(pos, 'default:furnace')
      -- stop timer on the inactive furnace
      minetest.get_node_timer(pos):stop()
      meta:set_string('super', 'maybe')
   end
   local infotext
   if active then
      infotext = S('Furnace active')
   else
      infotext = S('Furnace inactive')
   end
   infotext = infotext .. '\n' .. S('(Item: @1; Fuel: @2)', item_state, fuel_state)

   --
   -- Set meta values
   --
   meta:set_float('fuel_totaltime', fuel_totaltime)
   meta:set_float('fuel_time', fuel_time)
   meta:set_float('src_time', src_time)
   meta:set_string('formspec', formspec)
   meta:set_string('infotext', infotext)

   return result
end

--added by idiot12 from here


--
-- Node definitions
--

minetest.register_node('default:furnace', {
   description = S('Furnace'),
   tiles = {
      'default_furnace_top.png', 'default_furnace_bottom.png',
      'default_furnace_side.png', 'default_furnace_side.png',
      'default_furnace_side.png', 'default_furnace_front.png'
   },
   paramtype2 = 'facedir',
   groups = {cracky=2},
   legacy_facedir_simple = true,
   is_ground_content = false,
   sounds = default.node_sound_stone_defaults(),

   can_dig = can_dig,

   on_timer = furnace_node_timer,

   on_construct = function(pos)
      local meta = minetest.get_meta(pos)
      meta:set_string('super', 'maybe')
      local inv = meta:get_inventory()
      inv:set_size('src', 1)
      inv:set_size('fuel', 1)
      inv:set_size('dst', 4)
      furnace_node_timer(pos, 0)
   end,

   on_metadata_inventory_move = function(pos)
      minetest.get_node_timer(pos):start(1.0)
   end,
   on_metadata_inventory_put = function(pos)
      -- start timer function, it will sort out whether furnace can burn or not.
      minetest.get_node_timer(pos):start(1.0)

   end,
   on_blast = function(pos)
      local drops = {}
      default.get_inventory_drops(pos, 'src', drops)
      default.get_inventory_drops(pos, 'fuel', drops)
      default.get_inventory_drops(pos, 'dst', drops)
      drops[#drops+1] = 'default:furnace'
      minetest.remove_node(pos)
      return drops
   end,

   allow_metadata_inventory_put = allow_metadata_inventory_put,
   allow_metadata_inventory_move = allow_metadata_inventory_move,
   allow_metadata_inventory_take = allow_metadata_inventory_take,
})

minetest.register_node('default:furnace_active', {
   description = S('Furnace'),
   tiles = {
      'default_furnace_top.png', 'default_furnace_bottom.png',
      'default_furnace_side.png', 'default_furnace_side.png',
      'default_furnace_side.png',
      {
         image = 'default_furnace_front_active.png',
         backface_culling = false,
         animation = {
            type = 'vertical_frames',
            aspect_w = 16,
            aspect_h = 16,
            length = 1.5
         },
      }
   },
   paramtype2 = 'facedir',
   light_source = 8,
   drop = 'default:furnace',
   groups = {cracky=2, not_in_creative_inventory=1},
   legacy_facedir_simple = true,
   is_ground_content = false,
   sounds = default.node_sound_stone_defaults(),
   on_timer = furnace_node_timer,

   can_dig = can_dig,

   allow_metadata_inventory_put = allow_metadata_inventory_put,
   allow_metadata_inventory_move = allow_metadata_inventory_move,
   allow_metadata_inventory_take = allow_metadata_inventory_take,
})

minetest.register_craft({
   output = 'default:furnace',
   recipe = {
      {'group:stone', 'group:stone', 'group:stone'},
      {'group:stone', '', 'group:stone'},
      {'group:stone', 'group:stone', 'group:stone'},
   }
})
